#include "iuPacketEthernet.hpp"
#include <arpa/inet.h>
#include "iuPacketFactory.hpp"

/**
 * @brief Initialise la trame Ethernet
 */
iuPacketEthernet::iuPacketEthernet()
{
   m_type = 0;
   m_data = NULL;
}

/**
 * @brief Detruit la trame Ethernet
 */
iuPacketEthernet::~iuPacketEthernet()
{
   delete m_data;
}

/**
 * @brief Calcule la longueur de la trame Ethernet. Lorsque fromBuffer() est appelee, le buffer
 * passe en parametre devra avoir au moins cette taille
 * @return Renvoie la longueur de la trame Ethernet
 */
uint16_t iuPacketEthernet::getBufferLen() const
{
   uint16_t len;
   
   //Destination + Source + Type + CRC32 + longueur-donnees
   len = 6 + 6 + 2 + 4;
   if (m_data)
      len += m_data->getBufferLen();
   
   return len;
}

/**
 * @brief Lit la trame Ethernet a partir d'un buffer. Si les donnees correspondent a un
 * protocole connu, les donnees seront egalement lues
 * @param buffer le buffer ou la trame Ethernet sera lue
 * @param len la longueur de la trame Ethernet a lire
 * @return Renvoie le nombre d'octets lus (ici, len)
 * @note Si des donnees sont deja existantes dans la trame actuelle, elles ne seront pas detruites
 */
uint16_t iuPacketEthernet::fromBuffer(const char *buffer, uint16_t len)
{
   const char *b = buffer;
   
   //Lit l'adresse de destination, de source et le type des donnees
   b += m_dest.fromBuffer(b);
   b += m_src.fromBuffer(b);
   READ16(m_type, b);
   
   //Lit les donnees eventuelles
   if ((m_data = iuPacketFactory::create(2, m_type)))
      m_data->fromBuffer(b, len - (b - buffer) - 4);
   
   //Ignore le CRC32
   b = buffer + len - 4;
   b += 4;
   
   return (b - buffer);
}

/**
 * @brief Ecrit la trame Ethernet dans le buffer. Le buffer devra avoir une taille d'au
 * moins getBufferLen() octets
 * @param buffer l'adresse du debut du buffer dans lequel sera ecrit la trame Ethernet
 * @return Renvoie le nombre d'octets ecrits (== getBufferLen())
 */
uint16_t iuPacketEthernet::toBuffer(char *buffer) const
{
   char *b = buffer;
   
   //Ecrit l'adresse de destination, de source et le type des donnees
   b += m_dest.toBuffer(b);
   b += m_src.toBuffer(b);
   WRITE16(b, m_type);
   
   //Ecrit les donnees eventuelles
   if (m_data)
      b += m_data->toBuffer(b);
   
   //Ecrit un CRC32 de 0
   WRITE32(b, 0);
   
   return (b - buffer);
}

/**
 * @brief Attribue l'adresse de destination de la trame Ethernet
 * @param dest l'adresse de destination a attribuer
 */
void iuPacketEthernet::setDestination(const iuMacAddress &dest)
{
   m_dest = dest;
}

/**
 * @brief Recupere l'adresse de destination de la trame Ethernet
 * @return Renvoie l'adresse de destination de la trame Ethernet
 */
const iuMacAddress &iuPacketEthernet::getDestination() const
{
   return m_dest;
}

/**
 * @brief Attribue l'adresse source de la trame Ethernet
 * @param dest l'adresse source a attribuer
 */
void iuPacketEthernet::setSource(const iuMacAddress &src)
{
   m_src = src;
}

/**
 * @brief Recupere l'adresse source de la trame Ethernet
 * @return Renvoie l'adresse source de la trame Ethernet
 */
const iuMacAddress &iuPacketEthernet::getSource() const
{
   return m_src;
}

/**
 * @brief Attribue le type des donnees encapsulees dans la trame (e.g. 0x800 pour IP, 0x806 pour ARP)
 * @param type le type des donnees a attribuer
 */
void iuPacketEthernet::setType(uint16_t type)
{
   m_type = type;
}

/**
 * @brief Recupere le type des donnees encapsulees dans la trame
 * @return Renvoie le type des donnees encapsulees dans la trame
 */
uint16_t iuPacketEthernet::getType() const
{
   return m_type;
}

/**
 * @brief Definit les donnees a encapsuler dans la trame Ethernet. Ces donnees seront automatiquement
 * detruites lorsque la trame Ethernet sera detruite
 * @param data les donnees a encapsuler
 * @note Si des donnees sont deja existantes dans la trame actuelle, elles ne seront pas detruites
 */
void iuPacketEthernet::setData(iuPacket *data)
{
   m_data = data;
}

/**
 * @brief Recupere les donnees encapsulees dans la trame Ethernet
 * @return Renvoie les donnees encapsulees dans la trame Ethernet, ou NULL s'il n'y en a pas
 */
iuPacket *iuPacketEthernet::getData() const
{
   return m_data;
}
